﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GrandwayLib.Meters.Interfaces;
using System.IO.Ports;
using System.Threading;


namespace GrandwayLib.Meters.Classes
{
    /// <summary>
    /// JW8402光开关
    /// </summary>
    public class OS_JW8402 : IOpticalSwitch
    {
        private bool isConnected = false; //是否已连接
        private SerialPortParam serialPortParam = new SerialPortParam(SerialPort.GetPortNames().Count() > 0 ? SerialPort.GetPortNames()[0] : "COM1", 9600); //串口参数,默认值为第一个可用端口，如果没有时，使用"COM1"
        private SerialPort serialPort = null; //串口对象
        private AutoResetEvent switchChannelEvent = new AutoResetEvent(false); //通道改变信号量
        private AutoResetEvent dataReceiveEvent = new AutoResetEvent(false); //数据到达信号量
        private StringBuilder receiveString = new StringBuilder(); //收到的字符串
        private System.Timers.Timer checkConnectionTimer = new System.Timers.Timer(1000); //定时器，每秒检查一下串口连接状态


        /// <summary>
        /// 无参构造器
        /// </summary>
        public OS_JW8402()
        {
            serialPort = new SerialPort();
            serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceived); //数据到达事件
            #region 启动一条分析线程,分析串口收到的数据
            Thread analysisDataThread = new Thread(AnalysisReceiveData);
            analysisDataThread.IsBackground = true;
            analysisDataThread.Start();
            #endregion
        }

        /// <summary>
        /// 机型名称为JW8402，该光开关为嘉慧提供的8端口
        /// 单模光开关
        /// </summary>
        public string Name => "JW8402";

        /// <summary>
        /// 连接的参数
        /// </summary>
        public object ConnectParam {
            get {return serialPortParam; }
            set {
                Type valueType = value.GetType();
                if (valueType.Equals(ParamType))
                {
                    serialPortParam = (SerialPortParam)value;
                }
            }
        }

        /// <summary>
        /// 通道数量为8
        /// </summary>
        public int ChannelCount => 8;

        /// <summary>
        /// 是否已连接
        /// </summary>
        public bool IsConnected => isConnected;

        /// <summary>
        /// 连接参数的类型
        /// </summary>
        public Type ParamType  => typeof(SerialPortParam); 

        /// <summary>
        /// 通道号对应的波长，key - 通道号，value - 波长
        /// </summary>
        public Dictionary<int, int> ChannelWavelength { get; set; }

        /// <summary>
        /// 连接后，异常断开会产生一个事件
        /// </summary>
        public event EventHandler BreakConnect;

        /// <summary>
        /// 连接光开关
        /// </summary>
        /// <returns>true - 连接成功、false - 连接失败</returns>
        public bool Connect()
        {
            serialPort.PortName = serialPortParam.Portname;
            serialPort.BaudRate = serialPortParam.Baudrate;
            try {
                serialPort.Open();
                serialPort.Write("<AD01_S_01>");
                bool isSwitch = switchChannelEvent.WaitOne(1000); //1秒内是否收到返回值，收到认为成功连接，否则认为连接失败
                if (isSwitch)
                {
                    isConnected = true;
                }
                checkConnectionTimer.Start(); //启动检查连接状态的定时器
                return isSwitch;
            } catch {
                return false;
            }
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void DisConnect()
        {
            try
            {
                checkConnectionTimer.Stop(); //停止检测连接状态
                serialPort.Close();
                isConnected = false;
            }
            catch (Exception ex) {
                throw ex;
            }
        }

        /// <summary>
        /// 切换光开关至指定波长
        /// </summary>
        /// <param name="wavelength">波长</param>
        /// <returns>是否切换成功</returns>
        public bool SwitchChannel(int wavelength)
        {
            if (!isConnected)
            {
                return false;
            }
            List<int> channelList = ChannelWavelength.Keys.ToList(); //通道号
            List<int> wavelengthList = ChannelWavelength.Values.ToList(); //波长
            int index = wavelengthList.IndexOf(wavelength);
            if (index == -1)
            {
                return false;
            }
            serialPort.Write($"<AD01_S_0{channelList[index]}>");
            bool isSwitch = switchChannelEvent.WaitOne(1000); //1秒内是否收到返回值，表示切换成功，否则表示切换失败
            return isSwitch;
        }

        //数据到达时，释放信号量，使分析线程继续执行
        private void DataReceived(object sender,SerialDataReceivedEventArgs args) {
            dataReceiveEvent.Set(); //收到数据，释放信号量
        }

        //分析收到的数据
        private void AnalysisReceiveData() {
            while (true)
            {
                dataReceiveEvent.WaitOne();
                receiveString.Append(serialPort.ReadExisting()); //把收到的数据追加在字符串尾部
                int receiveLength = receiveString.Length; //字符串长度
                #region 如果第一个字符不是<，则移除一个字符
                for (int i = 0; i < receiveLength; i++)
                {
                    if (receiveString[0].ToString().Equals("<"))
                    {
                        break;
                    }
                    receiveString = receiveString.Remove(0,1);
                }
                #endregion
                string dataString = receiveString.ToString();
                int endIndex = dataString.IndexOf(">"); //结束符所在位置
                if (endIndex == -1)
                {
                    continue;
                }
                //截取结束符前的字符串(含结束符>)
                string tempString = dataString.Substring(0,endIndex + 1);
                if (tempString.Equals("<AD01_OK>"))  //收到指定的返回值，释放信号量
                {
                    switchChannelEvent.Set();
                }
                receiveString = receiveString.Remove(0,endIndex + 1); //删除结束前的字符串(含结束符)
            }
        }

        //检测端口是否正常打开，如果不处于打开状态，产生一个事件
        private void CheckConnection(object sender,System.Timers.ElapsedEventArgs args) {
            if (!serialPort.IsOpen)
            {
                isConnected = false;
                BreakConnect?.Invoke(this, new EventArgs());
            }
        }
    }
}
